package com.xyy.saas.payment.adpater.jdCredit.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.xyy.saas.payment.adpater.jd.cardbin.AESUtils;
import com.xyy.saas.payment.adpater.jd.cardbin.RSAUtils;
import com.xyy.saas.payment.adpater.jd.common.JDConstants;
import com.xyy.saas.payment.adpater.jd.utils.JDRequestUtils;
import com.xyy.saas.payment.adpater.jd.vo.*;
import com.xyy.saas.payment.adpater.jd.vo.cardbin.CardBinRequest;
import com.xyy.saas.payment.adpater.jd.vo.sign.JDOneKeySignApplyRequest;
import com.xyy.saas.payment.adpater.jd.vo.sign.JDQueryOneKeySignBanksRequest;
import com.xyy.saas.payment.adpater.jd.vo.sign.JDQueryOneKeySignResultRequest;
import com.xyy.saas.payment.callback.core.result.CallbackRunner;
import com.xyy.saas.payment.common.Constants;
import com.xyy.saas.payment.common.RocketMqTopic;
import com.xyy.saas.payment.common.exception.PaymentException;
import com.xyy.saas.payment.cores.bo.jd.*;
import com.xyy.saas.payment.cores.enums.JDPayContractStatusEnum;
import com.xyy.saas.payment.cores.enums.JDPayTradeStatusEnum;
import com.xyy.saas.payment.cores.enums.ResultCodeEnum;
import com.xyy.saas.payment.cores.vo.ResultVO;
import com.xyy.saas.payment.cores.vo.jd.JDBankCardNotifyVo;
import com.xyy.saas.payment.dao.model.BankCardBind;
import com.xyy.saas.payment.dao.model.BankCardBindLog;
import com.xyy.saas.payment.service.BankCardBindLogService;
import com.xyy.saas.payment.service.BankCardBindService;
import com.xyy.saas.payment.util.ApolloUtil;
import com.xyy.saas.payment.util.LeafUtil;
import com.xyy.saas.payment.util.PojoConvertUtil;
import com.xyy.saas.payment.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.io.InputStream;
import java.security.interfaces.RSAPublicKey;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 对接京东接口服务类
 */
@Slf4j
@Component
public class JdCreditService {
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private LeafUtil leafUtil;
    @Autowired
    private CallbackRunner callbackRunner;
    @Autowired
    private BankCardBindService bankCardBindService;
    @Autowired
    private BankCardBindLogService bankCardBindLogService;

    @PostConstruct
    public void init() {
        try {
            getCert("jd_cardbin.cer");
            log.info("初始化秘钥信息完成：jd_cardbin.cer");
        } catch (Exception e) {
            throw new RuntimeException("初始化秘钥信息异常：jd_cardbin.cer");
        }
    }

    /**
     * 签约结果通知处理
     */
    public boolean dealOneKeySignResult(JDMerchantOneKeySignResultDto jdMerchantOneKeySignResultDto) {
        String logPrefix = "jdPay签约结果通知" + jdMerchantOneKeySignResultDto.getRequestNo();
        BankCardBind bankCardBind = bankCardBindService.queryByReqNo(jdMerchantOneKeySignResultDto.getRequestNo());
        if (null == bankCardBind) {
            log.info("{} 京东签约结果通知未匹配到记录，不进行后续处理 {}", logPrefix, jdMerchantOneKeySignResultDto);
            return true;
        }
        for (int i = 0; i < 5; i++) {
            try {
                dealOneKeySignResult(logPrefix, bankCardBind, jdMerchantOneKeySignResultDto);
                return true;
            } catch (PaymentException pe) {
                log.error("{} 处理失败： {} {} ", logPrefix, JSONObject.toJSON(jdMerchantOneKeySignResultDto), pe);
                try { Thread.sleep(300); } catch (InterruptedException e) {}
            }
        }
        return false;
    }

    /**
     * 处理一键签约结果
     */
    public int dealOneKeySignResult(String logPrefix, BankCardBind notifyDbCard, JDMerchantOneKeySignResultDto notifyJdCard) throws PaymentException {
        int status = JDPayContractStatusEnum.transJDPayContractStatus(notifyJdCard.getSignStatus());
        // 若京东返回签约状态为处理中，则直接返回
        if (status == JDPayContractStatusEnum.PROC.getCode()) {
            log.info("{} 回调绑卡状态为绑卡中，不进行后续处理：{}", logPrefix, JSONObject.toJSONString(notifyJdCard));
            return status;
        }
        // 若数据库状态为已绑卡完成，则直接返回
        if (null != notifyDbCard && notifyDbCard.getContractStatus().equals(JDPayContractStatusEnum.SUCC.getCode())) {
            log.info("{} 数据库绑卡状态为绑卡完成，不进行后续处理：{}", logPrefix, JSONObject.toJSONString(notifyJdCard));
            return status;
        }
        // 获取分布式锁
        String lockKey = RedisUtil.getJDContractProc(notifyDbCard.getMerchantNo(), notifyDbCard.getBindAccount());
        if (!redisUtil.tryLock(lockKey)) {
            throw new PaymentException(ResultCodeEnum.JD_BUSY_SIGN_RESULT);
        }
        try {
            // 将记录更改为生效
            notifyDbCard.setYn("Y");
            // 组装绑卡记录结果
            notifyDbCard.setBankPhone(notifyJdCard.getPhone());
            notifyDbCard.setCardNo(notifyJdCard.getCardNo());
            notifyDbCard.setContractNo(notifyJdCard.getContractNo());
            notifyDbCard.setContractStatus(status);
            notifyDbCard.setResultCode(notifyJdCard.getCode());
            notifyDbCard.setResultDesc(notifyJdCard.getMessage());
            if (StringUtils.isBlank(notifyDbCard.getBankName())) {
                notifyDbCard.setBankName(notifyJdCard.getBankName());
            }
            if (StringUtils.isBlank(notifyDbCard.getBankCode())) {
                notifyDbCard.setBankCode(notifyJdCard.getBankCode());
            }
            // 组装日志数据
            BankCardBindLog bankCardBindLog = JSON.parseObject(notifyDbCard.getExtent(), BankCardBindLog.class);
            bankCardBindLog.setContractReqNo(notifyDbCard.getContractReqNo());
            bankCardBindLog.setContractStatus(status);
            bankCardBindLog.setResultCode(notifyJdCard.getCode());
            bankCardBindLog.setResultDesc(notifyJdCard.getMessage());
            bankCardBindLog.setExtent(notifyDbCard.getExtent());
            // 若身份证号与已成功的不一致，更新此记录，记录日志后，不进行处理：merchantNo、BindAccount、contractStatus
            BankCardBind sucCards = bankCardBindService.queryOne(BankCardBind.builder().merchantNo(notifyDbCard.getMerchantNo()).bindAccount(notifyDbCard.getBindAccount()).build(), Arrays.asList(JDPayContractStatusEnum.SUCC.getCode(), JDPayContractStatusEnum.REMO.getCode()));
            if (null != sucCards && !notifyDbCard.getIdNo().equals(sucCards.getIdNo()) && !sucCards.getReqNo().equals(notifyDbCard.getReqNo())) {
                // 更新此记录
                notifyDbCard.setResultCode(ResultCodeEnum.JD_CONTRACT_IDNO_ERROR.getCode());
                notifyDbCard.setResultDesc(ResultCodeEnum.JD_CONTRACT_IDNO_ERROR.getDisplayMsg());
                notifyDbCard.setContractStatus(JDPayContractStatusEnum.FAIL.getCode());
                notifyDbCard.setYn("N");
                bankCardBindService.update(notifyDbCard);
                // 解绑此记录
                cancelContract(logPrefix, JDCancelContractRequest.builder().reqNo(notifyDbCard.getReqNo()).contractNo(notifyJdCard.getContractNo()).merchantNo(notifyJdCard.getMerchantNo()).build());
                // 记录日志
                Map<String, String> extent = new HashMap<>();
                extent.put("oldIdNo", sucCards.getIdNo());
                extent.put("requestIdNo", notifyDbCard.getIdNo());
                log.info("{} 身份证号与已签约成功身份证号不匹配：{}，保存日志：{}，不进行后续处理", logPrefix, JSON.toJSONString(extent), JSON.toJSONString(notifyJdCard));
                bankCardBindLog.setResultCode(ResultCodeEnum.JD_CONTRACT_IDNO_ERROR.getCode());
                bankCardBindLog.setResultDesc(ResultCodeEnum.JD_CONTRACT_IDNO_ERROR.getDisplayMsg());
                bankCardBindLog.setContractStatus(JDPayContractStatusEnum.FAIL.getCode());
                bankCardBindLog.setExtent(JSON.toJSONString(extent));
                bankCardBindLogService.insert(bankCardBindLog);
                return status;
            }
            boolean updateExist = false;
            // 查询签约记录 : merchantNo、bindAccount、bankCode、cardNo：已签约、已解绑、失败
            BankCardBind cardBind = BankCardBind.builder().merchantNo(notifyDbCard.getMerchantNo()).bindAccount(notifyDbCard.getBindAccount()).bankCode(notifyJdCard.getBankCode()).cardNo(notifyJdCard.getCardNo()).build();
            BankCardBind existCard = bankCardBindService.queryOne(cardBind, Arrays.asList(JDPayContractStatusEnum.SUCC.getCode(), JDPayContractStatusEnum.REMO.getCode(), JDPayContractStatusEnum.FAIL.getCode()));
            bankCardBindLog.setContractReqNo(null != existCard ? existCard.getContractReqNo() : bankCardBindLog.getContractReqNo());
            notifyDbCard.setContractReqNo(bankCardBindLog.getContractReqNo());
            if (null != existCard && !notifyDbCard.getReqNo().equals(existCard.getReqNo())) {
                if (JDPayContractStatusEnum.SUCC.getCode() == existCard.getContractStatus()) {
                    log.info("{} 绑卡信息已存在绑卡成功记录，解绑后更新记录并返回，不进行后续处理：{} ", logPrefix, JSONObject.toJSONString(notifyJdCard));
                    cancelContract(logPrefix, JDCancelContractRequest.builder().reqNo(notifyDbCard.getReqNo()).contractNo(notifyJdCard.getContractNo()).merchantNo(notifyJdCard.getMerchantNo()).build());
                    notifyDbCard.setResultCode(ResultCodeEnum.JD_CONTRACT_SUCC.getCode());
                    notifyDbCard.setResultDesc(ResultCodeEnum.JD_CONTRACT_SUCC.getDisplayMsg());
                    notifyDbCard.setContractStatus(JDPayContractStatusEnum.SUCC.getCode());
                    notifyDbCard.setYn("N");
                    bankCardBindService.update(notifyDbCard);
                    return JDPayContractStatusEnum.SUCC.getCode();
                } else {
                    // 若原绑卡记录已解约或者失败，则失效
                    existCard.setYn("N");
                    updateExist = true;
                }
            }
            // 更新结果
            updateResult(updateExist ? existCard : null, notifyDbCard, bankCardBindLog);
            // 若正常流程：通知商城实名信息
            notifyShopping(notifyDbCard);
            return status;
        } finally {
            redisUtil.unLock(lockKey);
        }
    }

    /**
     * 更新结果
     */
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 36000, rollbackFor = Exception.class)
    public void updateResult(BankCardBind oldCard, BankCardBind newCard, BankCardBindLog bankCardBindLog) {
        // 更新原数据
        if (null != oldCard) {
            bankCardBindService.update(oldCard);
        }
        // 处理新数据
        if (null != newCard) {
            if (null != newCard.getId()) {
                bankCardBindService.update(newCard);
            } else {
                bankCardBindService.insert(newCard);
            }
        }
        // 保存签约日志记录
        if (null != bankCardBindLog) {
            bankCardBindLogService.insert(bankCardBindLog);
        }
    }

    /**
     * 查询一键签约银行列表接口
     */
    public JDQueryOneKeySignBanksDto queryOneKeySignBanks(String logPrefix, JDQueryOneKeySignBanksRequest jdQueryOneKeySignBanksRequest) throws Exception {
        String result = redisUtil.get(Constants.ONE_KEY_SIGN_BANK + jdQueryOneKeySignBanksRequest.getUserId());
        if (StringUtils.isNotEmpty(result)) {
            return JSON.parseObject(JSON.parse(result).toString(), JDQueryOneKeySignBanksDto.class);
        }

        // 公共请求参数
        Map<String, String> reqMap = buildCommonParamMap(jdQueryOneKeySignBanksRequest.getMerchantNo());
        // 拼装业务数据
        if (StringUtils.isBlank(jdQueryOneKeySignBanksRequest.getRequestNo())) {
            jdQueryOneKeySignBanksRequest.setRequestNo(leafUtil.getSnowflakeId());
        }
        // 请求接口
        String responseText = JDRequestUtils.tradeRequestSSL(logPrefix, jdQueryOneKeySignBanksRequest.getMerchantNo(), reqMap, JSONObject.toJSONString(jdQueryOneKeySignBanksRequest), JDConstants.URL_queryOneKeySignBanks);
        // 验证签名并获取返回结果
        JSONObject jsonObject = JDRequestUtils.verifySignAndReturn(logPrefix, jdQueryOneKeySignBanksRequest.getMerchantNo(), responseText);
        JDQueryOneKeySignBanksDto responseVo = JSON.toJavaObject(jsonObject, JDQueryOneKeySignBanksDto.class);
//        log.info("{} 查询一键签约银行列表结果为：{}", logPrefix, JSONObject.toJSONString(responseVo));
        if (!JDConstants.SUCCESS_CODE.equals(responseVo.getCode())) {
            throw new PaymentException(responseVo.getCode(), JDConstants.transMsg(logPrefix, responseVo.getCode(), responseVo.getMessage()));
        }
        // 缓存一键绑卡签约结果
        redisUtil.set(Constants.ONE_KEY_SIGN_BANK + jdQueryOneKeySignBanksRequest.getUserId(),
                JSONObject.toJSONString(responseVo), RedisUtil.MINUTE_10);
        return responseVo;
    }

    /**
     * 一键签约申请接口
     */
    public JDOneKeySignApplyDto oneKeySignApply(String logPrefix, JDOneKeySignApplyRequest jdOneKeySignApplyRequest) throws Exception {
        // 公共请求参数
        Map<String, String> reqMap = buildCommonParamMap(jdOneKeySignApplyRequest.getMerchantNo());
        // 业务请求参数组装
        if (StringUtils.isBlank(jdOneKeySignApplyRequest.getBizOrderId())) {
            jdOneKeySignApplyRequest.setBizOrderId(leafUtil.getSnowflakeId());
        }
        if (StringUtils.isBlank(jdOneKeySignApplyRequest.getRequestNo())) {
            jdOneKeySignApplyRequest.setRequestNo(leafUtil.getSnowflakeId());
        }
        if (StringUtils.isBlank(jdOneKeySignApplyRequest.getUserLicense())) {
            jdOneKeySignApplyRequest.setUserLicense("1");
        }
        if (StringUtils.isBlank(jdOneKeySignApplyRequest.getSignResultCallBackUrl())) {
            jdOneKeySignApplyRequest.setSignResultCallBackUrl(ApolloUtil.getProperty("jd.notifyUrl") + Constants.CHANNEL_JD_CARD_NAME + "/" + JDConstants.CALLBACK_CONTRACT_METHOD_PREFIX);
        }
        if (StringUtils.isBlank(jdOneKeySignApplyRequest.getIdType())) {
            jdOneKeySignApplyRequest.setIdType("ID");
        }
        // 将传入的url 改为 url/reqNo ，用于跳转后查询签约结果
        jdOneKeySignApplyRequest.setSignPageCallBackUrl(jdOneKeySignApplyRequest.getSignPageCallBackUrl() + "/" + jdOneKeySignApplyRequest.getRequestNo());
        // 请求接口
        String responseText = JDRequestUtils.tradeRequestSSL(logPrefix, jdOneKeySignApplyRequest.getMerchantNo(), reqMap, JSONObject.toJSONString(jdOneKeySignApplyRequest), JDConstants.URL_oneKeySignApply);
        //验证签名并获取返回结果
        JSONObject jsonObject = JDRequestUtils.verifySignAndReturn(logPrefix, jdOneKeySignApplyRequest.getMerchantNo(), responseText);
        JDOneKeySignApplyDto responseVo = JSON.toJavaObject(jsonObject, JDOneKeySignApplyDto.class);
        log.info("{} 一键签约申请结果为：{}", logPrefix, JSONObject.toJSONString(responseVo));
        if (!JDConstants.SUCCESS_CODE.equals(responseVo.getCode())) {
            throw new PaymentException(responseVo.getCode(), JDConstants.transMsg(logPrefix, responseVo.getCode(), responseVo.getMessage()));
        }
        return responseVo;
    }

    /**
     * 签约结果查询接口
     */
    public JDMerchantOneKeySignResultDto queryOneKeySignResult(String logPrefix, JDQueryOneKeySignResultRequest jdQueryOneKeySignResultRequest) throws Exception {
        // 公共请求参数
        Map<String, String> reqMap = buildCommonParamMap(jdQueryOneKeySignResultRequest.getMerchantNo());
        // 请求接口
        String responseText = JDRequestUtils.tradeRequestSSL(logPrefix, jdQueryOneKeySignResultRequest.getMerchantNo(), reqMap, JSONObject.toJSONString(jdQueryOneKeySignResultRequest), JDConstants.URL_queryOneKeySignResult);
        // 验证签名并获取返回结果<需要解密>
        JSONObject jsonObject = JDRequestUtils.verifySignAndDecryptReturn(logPrefix, jdQueryOneKeySignResultRequest.getMerchantNo(), responseText);
        JDMerchantOneKeySignResultDto responseVo = JSON.toJavaObject(jsonObject, JDMerchantOneKeySignResultDto.class);
        log.info("{} 一键签约结果查询结果为：{}", logPrefix, JSONObject.toJSONString(responseVo));
        if (!JDConstants.SUCCESS_CODE.equals(responseVo.getCode())) {
            throw new PaymentException(responseVo.getCode(), JDConstants.transMsg(logPrefix, responseVo.getCode(), responseVo.getMessage()));
        }
        return responseVo;
    }

    /**
     * 协议签约接口
     */
    public JDContractSignDto contractSign(String logPrefix, JDContractSignRequest jdContractSignRequest) throws Exception {
        // 公共请求参数
        Map<String, String> reqMap = buildCommonParamMap(jdContractSignRequest.getMerchantNo());
        // 业务请求参数组装
        if (StringUtils.isBlank(jdContractSignRequest.getContractReqNo())) {
            jdContractSignRequest.setContractReqNo(leafUtil.getSnowflakeId());
        }
        if (StringUtils.isBlank(jdContractSignRequest.getIdType())) {
            jdContractSignRequest.setIdType("ID");
        }
        // 请求接口
        String responseText = JDRequestUtils.tradeRequestSSL(logPrefix, jdContractSignRequest.getMerchantNo(), reqMap, JSONObject.toJSONString(jdContractSignRequest), JDConstants.URL_contractSign);
        // 验证签名并获取返回结果
        JSONObject jsonObject = JDRequestUtils.verifySignAndReturn(logPrefix, jdContractSignRequest.getMerchantNo(), responseText);
        JDContractSignDto responseVo = JSON.toJavaObject(jsonObject, JDContractSignDto.class);
        log.info("{} 签约请求结果为：{}", logPrefix, JSONObject.toJSONString(responseVo));
        if (!JDConstants.SUCCESS_CODE.equals(responseVo.getResultCode())) {
            throw new PaymentException(responseVo.getResultCode(), JDConstants.transMsg(logPrefix, responseVo.getResultCode(), responseVo.getResultDesc()));
        }
        return responseVo;
    }

    /**
     * 签约确认接口
     */
    public JDContractSignConfirmDto contractSignConfirm(String logPrefix, JDContractSignConfirmRequest jdContractSignConfirmRequest) throws Exception {
        // 公共请求参数
        Map<String, String> reqMap = buildCommonParamMap(jdContractSignConfirmRequest.getMerchantNo());
        // 业务请求参数组装
        if (StringUtils.isBlank(jdContractSignConfirmRequest.getReqNo())) {
            jdContractSignConfirmRequest.setReqNo(leafUtil.getSnowflakeId());
        }
        // 请求接口
        String responseText = JDRequestUtils.tradeRequestSSL(logPrefix, jdContractSignConfirmRequest.getMerchantNo(),reqMap, JSONObject.toJSONString(jdContractSignConfirmRequest), JDConstants.URL_contractSignConfirm);
        // 验证签名并获取返回结果
        JSONObject jsonObject = JDRequestUtils.verifySignAndReturn(logPrefix, jdContractSignConfirmRequest.getMerchantNo(), responseText);
        JDContractSignConfirmDto responseVo = JSON.toJavaObject(jsonObject, JDContractSignConfirmDto.class);
        log.info("{} 签约确认结果为：{}", logPrefix, JSONObject.toJSONString(responseVo));
        if (!JDConstants.SUCCESS_CODE.equals(responseVo.getResultCode())) {
            throw new PaymentException(responseVo.getResultCode(), JDConstants.transMsg(logPrefix, responseVo.getResultCode(), responseVo.getResultDesc()));
        }
        return responseVo;
    }

    /**
     * 协议解约接口：使用异步方式
     */
    public static ThreadPoolExecutor CONTRACT_EXECUTOR = new ThreadPoolExecutor(1,5, 10L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(50), new ThreadPoolExecutor.DiscardPolicy());
    public JDCancelContractDto cancelContract(String logPrefix, JDCancelContractRequest jdCancelContractRequest) {
        CONTRACT_EXECUTOR.execute(() -> {
            try {
            // 公共请求参数
            Map<String, String> reqMap = buildCommonParamMap(jdCancelContractRequest.getMerchantNo());
            // 业务请求参数组装
            if (StringUtils.isBlank(jdCancelContractRequest.getReqNo())) {
                jdCancelContractRequest.setReqNo(leafUtil.getSnowflakeId());
            }
            // 请求接口
            String responseText = JDRequestUtils.tradeRequestSSL(logPrefix, jdCancelContractRequest.getMerchantNo(), reqMap, JSONObject.toJSONString(jdCancelContractRequest), JDConstants.URL_cancelContract);
            // 验证签名并获取返回结果
            JSONObject jsonObject = JDRequestUtils.verifySignAndReturn(logPrefix, jdCancelContractRequest.getMerchantNo(), responseText);
            JDCancelContractDto responseVo = JSON.toJavaObject(jsonObject, JDCancelContractDto.class);
            log.info("{} 协议号解约结果为：{}", logPrefix, JSONObject.toJSONString(responseVo));
            } catch (Exception e) {
                log.error("{} 协议解约失败：{} , {}", logPrefix, jdCancelContractRequest, e);
            }
        });
        return JDCancelContractDto.builder().resultCode(JDConstants.SUCCESS_CODE).resultDesc("SUCCESS").build();
    }

    /**
     * 协议号支付
     */
    public JDSaleByContractResponse saleByContractNo(String logPrefix, JDSaleByContractRequest jdSaleByContractRequest) throws Exception {
        log.info("{} 协议支付请求:{}", logPrefix, JSON.toJSONString(jdSaleByContractRequest));
        // 公共请求参数
        Map<String, String> reqMap = buildCommonParamMap(jdSaleByContractRequest.getMerchantNo());
        // 业务请求参数组装
        if (StringUtils.isBlank(jdSaleByContractRequest.getNotifyUrl())) {
            jdSaleByContractRequest.setNotifyUrl(ApolloUtil.getProperty("jd.notifyUrl") + Constants.CHANNEL_JD_CARD_NAME + "/" + Constants.METHOD_PAY_SUCCESS_NAME);
        }
        jdSaleByContractRequest.setBizTp(JDConstants.BIZ_TYPE);
        // 设置分账支付信息
        List<JDDivisionAccSubTradeInfo> jdDivisionAccSubTradeInfoList = new ArrayList<>();
        jdDivisionAccSubTradeInfoList.add(JDDivisionAccSubTradeInfo.builder().tradeMoney(jdSaleByContractRequest.getTradeAmount()).outTradeNo(jdSaleByContractRequest.getOutTradeNo()).merchantNo(JDConstants.merchantSubMerchant(logPrefix, jdSaleByContractRequest.getMerchantNo())).build());
        jdSaleByContractRequest.setDivisionAccountInfo(JSON.toJSONString(jdDivisionAccSubTradeInfoList));
        // 请求接口
        String responseText = JDRequestUtils.tradeRequestSSL(logPrefix, jdSaleByContractRequest.getMerchantNo(),reqMap, JSONObject.toJSONString(jdSaleByContractRequest), JDConstants.URL_saleByContractNo);
        // 验证签名并获取返回结果
        JSONObject jsonObject = JDRequestUtils.verifySignAndReturn(logPrefix, jdSaleByContractRequest.getMerchantNo(), responseText);
        JDSaleByContractResponse responseVo = JSON.toJavaObject(jsonObject, JDSaleByContractResponse.class);
        log.info("{} 协议号支付结果为：{}", logPrefix, JSONObject.toJSONString(responseVo));
        if (!JDConstants.SUCCESS_CODE.equals(responseVo.getResultCode())) {
            throw new PaymentException(responseVo.getResultCode(), JDConstants.transMsg(logPrefix, responseVo.getResultCode(), responseVo.getResultDesc()));
        }
        if (JDPayTradeStatusEnum.CLOS.getCode().equals(responseVo.getTradeStatus())) {
            throw new PaymentException(ResultCodeEnum.JD_PAY_ERROR.getCode(), ResultCodeEnum.JD_PAY_ERROR.getDisplayMsg());
        }
        return responseVo;
    }

    /**
     * 协议退款接口
     */
    public JDTradeRefundResponseVo tradeRefund(String logPrefix, JDTradeRefundRequestVo jdTradeRefundRequestVo) throws Exception {
        // 公共请求参数
        Map<String, String> reqMap = buildCommonParamMap(jdTradeRefundRequestVo.getMerchantNo());
        // 业务请求参数组装
        if (StringUtils.isBlank(jdTradeRefundRequestVo.getNotifyUrl())) {
            jdTradeRefundRequestVo.setNotifyUrl(ApolloUtil.getProperty("jd.notifyUrl") + Constants.CHANNEL_JD_CARD_NAME + "/" + Constants.METHOD_REFUND_SUCCESS);
        }
        // 设置分账退款信息
        List<JDDivisionAccountRefundInfo> jdDivisionAccountRefundInfoList = new ArrayList<>();
        jdDivisionAccountRefundInfoList.add(JDDivisionAccountRefundInfo.builder().refundMoney(jdTradeRefundRequestVo.getRefundAmount()).originalOutTradeNo(jdTradeRefundRequestVo.getOrignalOutTradeNo()).refundOutTradeNo(jdTradeRefundRequestVo.getOutTradeNo()).merchantNo(JDConstants.merchantSubMerchant(logPrefix, jdTradeRefundRequestVo.getMerchantNo())).build());
        jdTradeRefundRequestVo.setDivisionAccountRefundInfo(JSON.toJSONString(jdDivisionAccountRefundInfoList));
        // 请求接口
        String responseText = JDRequestUtils.tradeRequestSSL(logPrefix, jdTradeRefundRequestVo.getMerchantNo(),reqMap, JSONObject.toJSONString(jdTradeRefundRequestVo), JDConstants.URL_tradeRefund);
        // 验证签名并获取返回结果
        JSONObject jsonObject = JDRequestUtils.verifySignAndReturn(logPrefix, jdTradeRefundRequestVo.getMerchantNo(), responseText);
        JDTradeRefundResponseVo responseVo = JSON.toJavaObject(jsonObject, JDTradeRefundResponseVo.class);
        log.info("{} 协议号退款结果为：{}", logPrefix, JSONObject.toJSONString(responseVo));
        if (!JDConstants.SUCCESS_CODE.equals(responseVo.getResultCode())) {
            throw new PaymentException(responseVo.getResultCode(), JDConstants.transMsg(logPrefix, responseVo.getResultCode(), responseVo.getResultDesc()));
        }
        if (JDPayTradeStatusEnum.CLOS.getCode().equals(responseVo.getTradeStatus())) {
            throw new PaymentException(ResultCodeEnum.JD_REFUND_ERROR.getCode(), ResultCodeEnum.JD_REFUND_ERROR.getDisplayMsg());
        }
        return responseVo;
    }

    /**
     * 协议支付、退款查询
     */
    public JDTradeQueryResponseVo tradeQuery(String logPrefix, JDTradeQueryRequestVo jdTradeQueryRequestVo) throws Exception {
        // 公共请求参数
        Map<String, String> reqMap = buildCommonParamMap(jdTradeQueryRequestVo.getMerchantNo());
        // 请求接口
        String responseText = JDRequestUtils.tradeRequestSSL(logPrefix, jdTradeQueryRequestVo.getMerchantNo(),reqMap, JSONObject.toJSONString(jdTradeQueryRequestVo), JDConstants.URL_tradeQuery);
        // 验证签名并获取返回结果
        JSONObject jsonObject = JDRequestUtils.verifySignAndReturn(logPrefix, jdTradeQueryRequestVo.getMerchantNo(), responseText);
        JDTradeQueryResponseVo responseVo = JSON.toJavaObject(jsonObject, JDTradeQueryResponseVo.class);
        log.info("{} 协议号支付、退款查询结果为：{}", logPrefix, JSONObject.toJSONString(responseVo));
        if (!JDConstants.SUCCESS_CODE.equals(responseVo.getResultCode())) {
            throw new PaymentException(responseVo.getResultCode(), JDConstants.transMsg(logPrefix, responseVo.getResultCode(), responseVo.getResultDesc()));
        }
        return responseVo;
    }

    /**
     * 渠道卡信息服务接口
     */
    public JDCardBinDataDto cardbin(String logPrefix, CardBinRequest cardBinRequest) throws Exception {
        String url = JDConstants.URL_CARDBIN;
        String encryptKey = AESUtils.generateRandomKey();
        String encryptKeyEn = RSAUtils.encrypt(getCert("jd_cardbin.cer"), encryptKey);
        String sensitiveInfoEn = new AESUtils(encryptKey).encryptData("cardNo=" + cardBinRequest.getCardNo());
        HttpClient httpClient = new HttpClient();
        httpClient.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, "utf-8");
        httpClient.getParams().setParameter(HttpMethodParams.SO_TIMEOUT, 30000);
        PostMethod postMethod = new PostMethod(url);
        postMethod.addParameter("service", "data.cardBin.check");
        postMethod.addParameter("version", "1.1");
        postMethod.addParameter("corpId", cardBinRequest.getMerchantNo());
        postMethod.addParameter("sensitiveInfo", sensitiveInfoEn);
        postMethod.addParameter("encryptKey", encryptKeyEn);
        log.info("{} cardbin请求地址：{}", logPrefix, url);
        log.info("{} cardbin请求参数: {}", logPrefix, JSON.toJSONString(postMethod.getParameters()));
        httpClient.executeMethod(postMethod);
        byte[] responseBody = postMethod.getResponseBody();
        String strResp = new String(responseBody, "UTF-8");
        log.info("{} cardbin请求返回结果: {}", logPrefix, strResp);
        JDCardBinDto responseVo = JSON.parseObject(strResp, JDCardBinDto.class);
        log.info("{} cardbin请求返回结果: {}", logPrefix, JSONObject.toJSONString(responseVo));
        if (!JDConstants.SUCCESS_CODE_CARBIN.equals(responseVo.getRespCode())) {
            throw new PaymentException(responseVo.getRespCode(), responseVo.getRespMsg());
        }
        if (!JDConstants.SUCCESS_CODE_CARBIN.equals(responseVo.getBizCode())) {
            throw new PaymentException(responseVo.getBizCode(), responseVo.getBizMsg());
        }
        return responseVo.getData();
    }

    // 锁
    private static Lock lock = new ReentrantLock();
    // map：存储密钥信息
    private static Map<String, RSAPublicKey> CERT_MAP = new ConcurrentHashMap<>();
    /**
     * 获取密钥信息
     */
    private static RSAPublicKey getCert(String certName) throws Exception {
        if (CERT_MAP.containsKey(certName)) {
            return CERT_MAP.get(certName);
        }
        InputStream inputStream;
        lock.lock();
        try {
            if (CERT_MAP.containsKey(certName)) {
                return CERT_MAP.get(certName);
            }
            inputStream = new ClassPathResource(certName).getInputStream();
            RSAPublicKey publicKey = RSAUtils.getPublicKey(inputStream);
            CERT_MAP.put(certName, publicKey);
            return publicKey;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 组装公共请求参数
     */
    private Map<String, String> buildCommonParamMap(String merchantNo) {
        // 公共请求参数
        Map<String, String> reqMap = new HashMap<>();
        // 二级商户号
        reqMap.put("merchantNo", merchantNo);
        // 请求唯一标识
        reqMap.put("reqNo", leafUtil.getSnowflakeId());
        // 字符集
        reqMap.put("charset", JDConstants.CHARSET_UTF8);
        // 数据类型
        reqMap.put(JDConstants.PARAM_FORMAT_TYPE, JDConstants.DATA_FORMAT_TYPE_JSON);
        // 签名类型
        reqMap.put(JDConstants.PARAM_SIGN_TYPE, JDConstants.SING_TYPE);
        // 加密类型
        reqMap.put(JDConstants.PARAM_ENC_TYPE, JDConstants.ENCRYPT_TYPE_AP7);
        return reqMap;
    }

    /**
     * 通知实名信息到商城侧
     */
    public void notifyShopping(BankCardBind bankCardBind) {
        if (!bankCardBind.getContractStatus().equals(JDPayContractStatusEnum.SUCC.getCode())) {
            log.info("[绑卡通知商城]绑卡信息不为成功，不进行通知商城侧数据:{}", bankCardBind);
            return;
        }
        JDBankCardNotifyVo jdBankCardNotifyVo = PojoConvertUtil.convertPojo(bankCardBind, JDBankCardNotifyVo::new);
        // 通知商城侧
        Map<String, Object> mqMsg = new HashMap<>(4);
        mqMsg.put("callBack", JDConstants.CONTRACT_CALLBACK);
        mqMsg.put("count", "0");
        mqMsg.put("msg", JSONObject.toJSONString(ResultVO.createSuccess(jdBankCardNotifyVo)));
        log.info("[绑卡通知商城]通知商城侧实名信息:{} - {}", bankCardBind, JSONObject.toJSONString(mqMsg));
        callbackRunner.syncSendDelayMsg(RocketMqTopic.TOPIC_RETRY_NOTIFY, mqMsg, 0);
    }

}
